<?php

Raise::load('RaiseTime');
Raise::load('core.helper.sorting.IRaiseComparable');

/**
 * RaiseTimeSpan class
 * definition of a period of time
 *
 * @author Sam-Mauris Yong / hellclanner at live dot com
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package phpraise.core.datetime
 * @since 1.2
 */
class RaiseTimeSpan extends RaiseTime implements IRaiseComparable {

    /**
     * Day of the month (1 to 31)
     * @var integer
     */
    private $day = 0;

    /**
     * Create a new RaiseTimeSpan based on a period of time in seconds
     * @param integer $a The period of time in seconds
     */
    public function __construct($a = 0){
        if($a < 0){
            throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
        }
        $this->day(floor($a / 86400));
        $a -= $this->day() * 86400;
        $this->hour(floor($a / 3600));
        $a -= $this->hour() * 3600;
        $this->minute(floor($a / 60));
        $a -= $this->minute() * 60;
        $this->second($a);
    }

    /**
     * The millisecond component of the RaiseDateTime
     * @param integer $a An integer that represents millisecond (1000th of a second)
     * @return integer
     */
    public function millisecond($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            parent::millisecond($a);
        }
        return parent::millisecond();
    }

    /**
     * The seconds component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 seconds in 1 minute
     * @return integer
     */
    public function second($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            parent::second($a);
        }
        return parent::second();
    }

    /**
     * The minute component of the RaiseDateTime
     * @param integer $a An integer from 0 to 59 that represents the 60 minutes in 1 hour
     * @return integer
     */
    public function minute($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            parent::minute($a);
        }
        return parent::minute();
    }

    /**
     * The hour component of the RaiseDateTime
     * @param integer $a An integer from 0 to 23 that represents 24 hours
     * @return integer
     */
    public function hour($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            parent::hour($a);
        }
        return parent::hour();
    }

    /**
     * The day component of the RaiseDateTime
     * @param integer $a An integer from 1 to 31 representing the days of a month
     * @return integer
     */
    public function day($a = null){
        if(func_num_args() == 1){
            if($a < 0){
                throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
            }
            $this->day = (int)$a;
        }
        return $this->day;
    }

    /**
     * Get the total number of seconds in this time span
     * @return integer|double
     */
    public function totalSeconds(){
        return $this->day * 86400.0 + $this->hour() * 3600.0 + $this->minute() * 60.0 + $this->second() + ($this->millisecond() / 1000.0);
    }

    /**
     * Get the total number of minutes in this time span
     * @return integer|double
     */
    public function totalMinutes(){
        return $this->day * 1440 + $this->hour() * 60.0 + $this->minute() + ($this->second() / 60.0) + ($this->millisecond() / 60000.0);
    }

    /**
     * Get the total number of hours in this time span
     * @return integer|double
     */
    public function totalHours(){
        return $this->day * 24 + $this->hour() + ($this->minute() / 60.0) + ($this->second() / 3600.0) + ($this->millisecond() / 3600000.0);
    }

    /**
     * Get the total number of days in this time span
     * @return integer|double
     */
    public function totalDays(){
        return $this->day + ($this->hour() / 24.0) + ($this->minute() / 1440.0) + ($this->second() / 86400.0) + ($this->millisecond() / 86400000.0);
    }

    /**
     * Compare to another RaiseTimeSpan
     * @param RaiseTimeSpan $o2 The other time span to compare with
     */
    public function compareTo($o2) {
        $comparator = new RaiseTimeSpanComparator();
        return $comparator->compare($this, $o2);
    }

    /**
     * Add with another RaiseTimeSpan
     * @param RaiseTimeSpan $o2 The amount of time to add
     * @return RaiseTimeSpan The resulting RaiseTimeSpan from the add operation
     */
    public function add($o2){
        $temp = $this->raiseClone();
        
        $limits = array(
            'millisecond' => 999,
            'second' => 59,
            'minute' => 59,
            'hour' => 23,
            'day' => null
        );
        
        $nextLimit = '';
        foreach($limits as $name => $limit){
            list($nextLimit,) = each($limits);
            $temp->$name($temp->$name() + $o2->$name());
            if($limit && $nextLimit && $temp->$name() > $limit){
                $temp->$nextLimit($temp->$nextLimit() + 1);
            }
        }
        
        return $temp;
    }

    /**
     * Subtract with another RaiseTimeSpan to give a new RaiseTimeSpan
     * @param RaiseTimeSpan $o2 The amount of time to deduct
     * @return RaiseTimeSpan The resulting RaiseTimeSpan from the subtract operation
     */
    public function subtract($o2){
        if($this->totalSeconds() < $o2->totalSeconds()){
            throw new RaiseInvalidArgumentException('A time period in negative value is not acceptable in RaiseTimeSpan.');
        }
        
        $components = array(
            'millisecond' => 1000,
            'second' => 60,
            'minute' => 60,
            'hour' => 24,
            'day' => null
        );
        
        $temp = $this->raiseClone();
        
        $next = null;
        foreach($components as $comp => $value){
            list($next,) = each($components);
            $tempValue = $temp->$comp() - $o2->$comp();
            if($tempValue < 0){
                $tempValue = $value + $tempValue;
                if($next){
                    $temp->$next($temp->$next() - 1);
                }
            }
            $temp->$comp($tempValue);
        }
        
        return $temp;
    }
    
}
